/*
 * FileName: epoll.cc
 * Author: 李波(Edwin Lee)
 * Date: 2024-04-24 21:42:08
 * Version: 1.0
 * Description: 
 */
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/fcntl.h>
#include <sys/epoll.h>
//初始化服务端的监听端口
int initserver(int port);

int main(int argc,char* argv[])
{
	if(argc!=2)
	{
		printf("usage:./epoll 6666\n");
		return -1;
	}
	
	//初始化服务端用于监听的socket
	int lfd = initserver(atoi(argv[1]));
	printf("listensock=%d\n",lfd);
	if(lfd<0)
	{
		printf("initserver() failed\n");
		return -1;
	}
	int epfd = epoll_create(1);
	char buf[1500];
	struct epoll_event event;
	event.events=EPOLLIN;
	event.data.fd=lfd;
	epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&event);
	struct epoll_event events[16];
	for(;;)
	{
		int ret = epoll_wait(epfd,events,16,-1);
		if(0 > ret)
		{
			perror("select failed");
			return -1;
		}
		else if (0==ret) 
		{
			printf("epoll_wait() timeout\n");
			continue;
		}
		for(int i=0; i<ret; ++i)
		{
			int fd = events[i].data.fd;
			if(fd == lfd)
			{
				struct sockaddr_in cliaddr;
				socklen_t len = sizeof cliaddr;
				int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &len);
				printf("new client ip=%s port=%d cfd=%d\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port),cfd);

				//貌似epoll_ctl中会复制一份event的内容，因此这里不需要new一个event
				event.data.fd = cfd;
				epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&event);
				continue;
			}

			memset(buf,0,1500);
			int nrecv = recv(fd,buf,1500,0);
			if(0>=nrecv)
			{
				printf("client close up fd=%d\n",fd);
				close(fd);
				epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
				continue;
			}
			printf("recv:%s\n",buf);
			send(fd,buf,nrecv,0);
		}

	}
	return 0;
}


int initserver(int port)
{
	int lfd = socket(AF_INET, SOCK_STREAM, 0);
	if(lfd<0)
	{
		perror("socket() failed");
		return -1;
	}

	//设置lfd的地址复用(未理解)
	int opt = 1;
	setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof opt);

	struct sockaddr_in servaddr;
	servaddr.sin_family = AF_INET;
	//设置任何人都能访问
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(port);
	
	if(bind(lfd,(struct sockaddr*)&servaddr,sizeof servaddr)<0)
	{
		perror("bind() failed");
		close(lfd);
		return -1;
	}

	if(listen(lfd,128)!=0)
	{
		perror("listen() failed");
		close(lfd);
		return -1;
	}
	
	return lfd;
}

